---
layout: ../../layouts/PageLayout.astro
title: Zod Schema Validation
description: VeeValidate Official Zod Schema Integration
order: 1
---

import DocTip from '@/components/DocTip.vue';

# Zod Schema Validation

<div class="mb-10 w-full flex items-center justify-center">
  <img class="h-40" src="https://github.com/logaretm/vee-validate/raw/main/logo.png" />

  <a class="ml-4" href="https://github.com/colinhacks/zod" target="_blank">
    <img class="h-40" src="https://github.com/colinhacks/zod/raw/master/logo.svg" />
  </a>
</div>

If you prefer to use [zod](https://github.com/colinhacks/zod) for schema validation instead of [yup](https://github.com/jquense/yup), you can do so with the `@vee-validate/zod` package.

With `@vee-validate/zod` you can use `Zod` typed schemas as drop-in replacements for `yup` schemas.

## Install

To use this plugin, make sure to install these packages `vee-validate`, `zod`, and `@vee-validate/zod`.

```sh
yarn add vee-validate zod @vee-validate/zod

# or with NPM
npm install vee-validate zod @vee-validate/zod

# or with Pnpm
pnpm add vee-validate zod @vee-validate/zod
```

## Validating with Zod

You can create validation schemas for either field-level validation or form-level validation, the `@vee-validate/zod` exposes a `toTypedSchema` function that transform Zod's schemas into something that can be understood by `vee-validate` main core and use them to perform validation. It also allows vee-validate to figure out the types for both the input values and the submitted values.

## Field-level Validation

The `toTypedSchema` function accepts a Zod schema. You can use the converted schema by passing it to the `rules` prop present on the `<Field />` component:

```vue
<template>
  <Form>
    <Field name="email" type="email" :rules="fieldSchema" />
    <ErrorMessage name="email" />
  </Form>
</template>

<script setup>
import { Field, Form, ErrorMessage } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import * as zod from 'zod';

const fieldSchema = toTypedSchema(zod.string().nonempty('Field is required').email('Must be a valid email'));
</script>
```

If you prefer to use the Composition API, then you can pass the converted schema to the `useField` function:

```vue
<template>
  <input name="email" v-model="value" type="email" />
  <span>{{ errorMessage }}</span>
</template>

<script setup>
import { useField } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import * as zod from 'zod';

const fieldSchema = toTypedSchema(
  zod.string().min(1, { message: 'Field is required' }).email({ message: 'Must be a valid email' })
);
const { value, errorMessage } = useField('email', fieldSchema);
</script>
```

## Form-Level Validation

You can also use Zod's `zod.object` to create validation schemas for your forms instead of individually passing it for each field, this is covered in general in the [form-level validation section](/guide/components/validation).

To be able to use `zod.object` to define form schemas, you will be using the same `toTypedSchema` function.

You can pass the converted schema to the `validation-schema` prop present on the `<Form />` component:

```vue
<template>
  <Form :validation-schema="validationSchema" @submit="onSubmit">
    <Field name="email" type="email" />
    <ErrorMessage name="email" />

    <Field name="password" type="password" />
    <ErrorMessage name="password" />

    <button>Submit</button>
  </Form>
</template>

<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import * as zod from 'zod';

const validationSchema = toTypedSchema(
  zod.object({
    email: zod.string().min(1, { message: 'This is required' }).email({ message: 'Must be a valid email' }),
    password: zod.string().min(1, { message: 'This is required' }).min(8, { message: 'Too short' }),
  })
);

function onSubmit(values) {
  alert(JSON.stringify(values, null, 2));
}
</script>
```

Alternatively, if you prefer to use the composition API, you can pass the converted schema as the `validationSchema` option accepted by the `useForm` function:

```vue
<template>
  <form @submit="onSubmit">
    <input name="email" v-model="email" type="email" />
    <span>{{ errors.email }}</span>

    <input name="password" v-model="password" type="password" />
    <span>{{ errors.password }}</span>

    <button>Submit</button>
  </form>
</template>

<script setup>
import { useField, useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import * as zod from 'zod';

const validationSchema = toTypedSchema(
  zod.object({
    email: zod.string().min(1, { message: 'This is required' }).email({ message: 'Must be a valid email' }),
    password: zod.string().min(1, { message: 'This is required' }).min(8, { message: 'Too short' }),
  })
);

const { handleSubmit, errors } = useForm({
  validationSchema,
});

const { value: email } = useField('email');
const { value: password } = useField('password');

const onSubmit = handleSubmit(values => {
  alert(JSON.stringify(values, null, 2));
});
</script>
```

<DocTip title="TypeScript Form Values Inference">

You can find more information on how typed schemas work in vee-validate [here](/guide/composition-api/typed-schema#zod)

</DocTip>

<DocTip title="refine/superRefine">

There is a known issue with Zod's `refine` and `superRefine` not executing whenever some object keys are missing which is common with forms. This is not an issue with vee-validate as it is a design choice in Zod at the moment. Refer to [this issue](https://github.com/logaretm/vee-validate/issues/4338) for explanations and further reading.

</DocTip>
